﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedNite;
using System.Diagnostics;
using System.Threading;

namespace MediaPortal.Plugins.Sense
{
    public class SensorHandler
    {
        protected XnMSessionManager sessionManager;
        protected XnMOpenNIContext context;
        protected XnMPointDenoiser denoiser;
        protected XnMSelectableSlider2D slider;
        protected XnMWaveDetector waveDetector;

        //protected XnMFlowRouter flowRouter;
        protected bool listening = false;
        protected bool listenForEndSession = false;

        protected Thread sensorThread;

        public event Action<SensorEvent, bool> ControlDetected;

        public virtual bool Start()
        {
            if (!this.listening)
            {
                this.listening = true;

                Logger.Info("Starting sensor.");

                if (sensorThread == null)
                {
                    sensorThread = new Thread(Run);
                    sensorThread.IsBackground = true;
                    sensorThread.Priority = ThreadPriority.BelowNormal;
                    sensorThread.Name = "SensorThread";
                    sensorThread.Start();
                }
                
                return true;
            }

            return false;
        }

        public virtual bool Stop()
        {
            if (this.listening)
            {
                Logger.Info("Stopping sensor.");
                this.listening = false;
                return true;
            }

            return false;
        }

        protected void Run()
        {
            try
            {
                Logger.Info("Initializing sensor.");
                this.context = new XnMOpenNIContext();
                this.context.Init();

                this.sessionManager = new XnMSessionManager(context, "Wave", "RaiseHand");
                this.sessionManager.FocusStartDetected += new EventHandler<FocusStartEventArgs>(OnFocusStartDetected);
                this.sessionManager.SessionStarted += new EventHandler<PointEventArgs>(OnSessionStarted);
                this.sessionManager.SessionEnded += new EventHandler(OnSessionEnded);

                this.waveDetector = new XnMWaveDetector();
                this.waveDetector.Wave += new EventHandler(OnWaveDetected);

                this.slider = new XnMSelectableSlider2D(7, 5);
                this.slider.HysteresisRatio = 0;
                this.slider.ItemHovered += new EventHandler<SelectableSlider2DHoverEventArgs>(OnSliderItemHovered);
                this.slider.ItemSelected += new EventHandler<SelectableSlider2DSelectEventArgs>(OnSliderItemSelected);
                
                this.denoiser = new XnMPointDenoiser(5);
                this.denoiser.AddListener(slider);
                this.denoiser.AddListener(waveDetector);

                //XnMPushDetector pushDetector = new XnMPushDetector();
                //pushDetector.Push += new EventHandler<PushDetectorEventArgs>(OnPushDetected);
                // pushDetector.PushImmediateMinimumVelocity = 0.4f;
                //XnMFlowRouter flowRouter = new XnMFlowRouter();
                //XnMBroadcaster broadcaster = new XnMBroadcaster();
                //broadcaster.AddListener(swipeDetector);
                //broadcaster.AddListener(pushDetector);

                this.sessionManager.AddListener(denoiser);

                Logger.Info("Sensor initialized and listening.");

                while (this.listening)
                {
                    this.context.Update();
                    this.sessionManager.Update(context);
                }
            }
            catch (Exception e)
            {
                Logger.Error("Sensor encountered and error.", e);
                this.listening = false;
                return;
            }
            finally
            {
                if (this.sessionManager != null)
                {
                    this.sessionManager.ClearQueue();
                    this.sessionManager = null;
                }

                if (this.context != null)
                {
                    this.context.Close();
                    this.context.Dispose();
                }

                if (this.waveDetector != null)
                {
                    this.waveDetector.Dispose();
                }

                if (this.slider != null)
                {
                    this.slider.Dispose();
                }

                if (this.denoiser != null)
                {
                    denoiser.Dispose();
                }                
            }

            Logger.Info("Sensor stopped listening.");
        }

        protected void OnSliderItemSelected(object sender, SelectableSlider2DSelectEventArgs e)
        {
            if (e.SelectDirection == Direction.Forward)
            {
                RaiseSensorEvent(SensorEvent.Select);
            }
            else if (e.SelectDirection == Direction.Backward)
            {
                    Logger.Debug("Listening for end session gesture.");
                    listenForEndSession = true;
            }
        }

        protected void OnSliderItemHovered(object sender, SelectableSlider2DHoverEventArgs e)
        {
            bool xIsNeutral = (2 < e.X && e.X < 5);
            bool yIsNeutral = (2 == e.Y);

            if (listenForEndSession && e.Y < 2)
            {
                Logger.Debug("User ended session.");
                listenForEndSession = false;
                sessionManager.EndSession();
                RaiseSensorEvent(SensorEvent.SessionEnd);
                return;
            }                

            if (e.Y < 2)
            {
                RaiseSensorEvent(SensorEvent.Down, (e.Y == 0));
            }
            else if (e.Y > 2)
            {
                RaiseSensorEvent(SensorEvent.Up, (e.Y == 4));
            }
            else if (e.X > 4)
            {
                RaiseSensorEvent(SensorEvent.Right, (e.X == 6));
            }
            else if (e.X < 2)
            {
                RaiseSensorEvent(SensorEvent.Left, (e.X == 0));
            }
            else
            {
                RaiseSensorEvent(SensorEvent.Neutral);
            }
        }  

        protected void OnWaveDetected(object sender, EventArgs e)
        {
            Logger.Debug("Wave detected");
            RaiseSensorEvent(SensorEvent.Back);
        }

        protected void OnPushDetected(object sender, PushDetectorEventArgs e)
        {
            Logger.Debug("Push detected: Velocity={0}", e.Velocity);
            RaiseSensorEvent(SensorEvent.Select);
        }

        protected void OnSessionEnded(object sender, EventArgs e)
        {
            Logger.Info("Session ended.");
            RaiseSensorEvent(SensorEvent.SessionEnd);
        }

        protected void OnSessionStarted(object sender, PointEventArgs e)
        {
            XnMSessionManager session = sender as XnMSessionManager;
            Logger.Info("Session started.");
            session.TrackPoint(e.Point);
            RaiseSensorEvent(SensorEvent.SessionStart);
        }

        protected void OnFocusStartDetected(object sender, FocusStartEventArgs e)
        {
            Logger.Debug("Focus start detected.");
        }

        protected void OnSwipeDetected(object sender, SwipeDetectorGeneralEventArgs e)
        {
            Logger.Debug("Swipe detected: Angle={0}, Velocity={1}, Direction={2}", e.Angle, e.Velocity, e.SelectDirection);
            switch(e.SelectDirection) 
            {
                case Direction.Up:
                    RaiseSensorEvent(SensorEvent.Up);
                    break;
                case Direction.Down:
                    RaiseSensorEvent(SensorEvent.Down);
                    break;
                case Direction.Left:
                    RaiseSensorEvent(SensorEvent.Left);
                    break;
                case Direction.Right:
                    RaiseSensorEvent(SensorEvent.Right);
                    break;
            }                
        }

        protected void RaiseSensorEvent(SensorEvent control)
        {
            RaiseSensorEvent(control, false);
        }

        protected void RaiseSensorEvent(SensorEvent control, bool repeat)
        {
            Logger.Debug("Raise: SensorEvent: {0} Repeat: {1}", control, repeat);

            if (listenForEndSession)
            {
                Logger.Debug("Cancelled end session gesture.");
                listenForEndSession = false;
            }

            if (ControlDetected != null)
            {
                ControlDetected(control, repeat);
            }
        }

    }
}
